/**
*  \file ihex.c
*
*  \brief
*
*  \author   <@linde-mh.de>
*
*
static state_colon()


   Byte 1       Header = colon(:)
   2..3         The number of data bytes in hex notation
   4..5         High byte of the record load address
   6..7         Low byte of the record load address
   8..9         Record type, must be "00"
   10..x        Data bytes in hex notation:
        x = (number of bytes - 1) * 2 + 11
   x+1..x+2     Checksum in hex notation
   x+3..x+4     Carriage return, line feed
*
*/

/* This is what Intel Hex files look like:

1. INTEL FORMATS

A. Intel 1

   16-bit address-field format, for files 64k bytes in length or less.

   DATA RECORD
   Byte 1       Header = colon(:)
   2..3         The number of data bytes in hex notation
   4..5         High byte of the record load address
   6..7         Low byte of the record load address
   8..9         Record type, must be "00"
   10..x        Data bytes in hex notation:
        x = (number of bytes - 1) * 2 + 11
   x+1..x+2     Checksum in hex notation
   x+3..x+4     Carriage return, line feed

   END RECORD
   Byte 1       Header = colon (:)
   2..3         The byte count, must be "00"
   4..7         Transfer-address (usually "0000")
                the jump-to address, execution start address
   8..9         Record type, must be "01"
   10..11       Checksum, in hex notation
   12..13       Carriage return, line feed

B. INTEL 2

   MCS-86 format, using a 20-bit address for files larger than 64K bytes.

   DATA RECORD
   Byte 1       Header = colon (:)
   2..3         The byte count of this record, hex notation
   4..5         High byte of the record load address
   6..7         Low byte of the record load address
   8..9         Record type, must be "00"
   10..x        The data bytes in hex notation:
        x = (number of data bytes - 1) * 2 + 11
   x+1..x+2     Checksum in hex notation
   x+3..x+4     Carriage return, line feed

   EXTENDED ADDRESS RECORD
   Byte 1       Header = colon(:)
   2..3         The byte count, must be "02"
   4..7         Load address, must be "0000"
   8..9         Record type, must be "02"
   10..11       High byte of the offset address
   12..13       Low byte of the offset address
   14..15       Checksum in hex notation
   16..17       Carriage return, line feed

   The checksums are the two's complement of the 8-bit sum
   without carry of the byte count, offset address, and the
   record type.

   START ADDRESS RECORD
   Byte 1       Header = colon (:)
   2..3         The byte count, must be "04"
   4..7         Load address, must be "0000"
   8..9         Record type, must be "03"
   10..13       8086 CS value
   14..17       8086 IP value
   18..19       Checksum in hex notation
   20..21       Carriage return, line feed

Another document reports these additional types:

   EXTENDED LINEAR ADDRESS RECORD
   Byte 1       Header = colon (:)
   2..3         The byte count, must be "02"
   4..7         Load address, must be "0000"
   8..9         Record type, must be "04"
   10..13       Upper 16 bits of address of subsequent records
   14..15       Checksum in hex notation
   16..17       Carriage return, line feed

   START LINEAR ADDRESS RECORD
   Byte 1       Header = colon (:)
   2..3         The byte count, must be "02"
   4..7         Load address, must be "0000"
   8..9         Record type, must be "05"
   10..13       Upper 16 bits of start address
   14..15       Checksum in hex notation
   16..17       Carriage return, line feed

The MRI compiler uses this, which is a repeat of type 5:

  EXTENDED START RECORD
   Byte 1       Header = colon (:)
   2..3         The byte count, must be "04"
   4..7         Load address, must be "0000"
   8..9         Record type, must be "05"
   10..13       Upper 16 bits of start address
   14..17       Lower 16 bits of start address
   18..19       Checksum in hex notation
   20..21       Carriage return, line feed.  */


/*===============================================[ private includes  ]================================================*/
#define IHEX_LINKAGE
#include "ihex.h"
#include <string.h>
#include <stdio.h>
/*===============================================[ private defines   ]================================================*/

/*===============================================[ private datatypes ]================================================*/
enum ihex_record
{
	ihex_record_data = 0x00,
	ihex_record_end  = 0x01,
	ihex_record_address =0x02,
	ihex_record_start_address =0x03,
	ihex_record_extended_linear =0x04,
	ihex_record_start_linear = 0x05
};

struct ihex_line
{
   U8 size;
   U16 address;
   U8 record_type;
   U8 data[0x100];
};


/*===============================================[ private variables ]================================================*/

/*===============================================[ public variables  ]================================================*/

/*===============================================[ private functions ]================================================*/
STATIC_INLINE RC ihex_read_line( FILE *fh, struct ihex_line *line );
STATIC_INLINE S16 ihex_read_byte( FILE *fh );
/*====================================================================================================================*/

struct section_list* ihex_load( FILE *fh )
{
	struct section_list* seclist;
	struct section *current_sec=NULL;
	U32 linear_address_offset=0;
	BO end_record_found=FALSE;

	seclist = section_list_create();

	while( !end_record_found )
	{
		struct ihex_line line;
		ihex_read_line( fh, &line );
		switch( line.record_type )
		{
			case ihex_record_data:
			{
				if( NULL == current_sec)
				{
					current_sec = section_create(0x10000);
					LIST_DATA(current_sec)->address=line.address+linear_address_offset;
					section_insert(seclist, current_sec );
				}

				if( (LIST_DATA(current_sec)->address+LIST_DATA(current_sec)->size )!= line.address+linear_address_offset )
				{
					current_sec = section_create(0x10000);
					LIST_DATA(current_sec)->address=line.address+linear_address_offset;
					section_insert(seclist, current_sec );
				}

				memcpy( &LIST_DATA(current_sec)->data[LIST_DATA(current_sec)->size], &line.data[0], line.size );
				LIST_DATA(current_sec)->size+=line.size;
				break;
			}
			case ihex_record_end:
			{
				end_record_found=TRUE;
				break;
			}
			case ihex_record_address:
			{
				break;

			}
			case ihex_record_start_address:
			{
				break;

			}
			case ihex_record_extended_linear:
			{

				linear_address_offset=line.data[0]<<24;
				linear_address_offset|=line.data[1]<<16;
				current_sec=NULL;
				break;

			}
			case ihex_record_start_linear:
			{
				break;

			}
		}

	}

	return seclist;
}


/*
 *    Byte 1       Header = colon(:)
   2..3         The number of data bytes in hex notation
   4..5         High byte of the record load address
   6..7         Low byte of the record load address
   8..9         Record type, must be "00"
   10..x        Data bytes in hex notation:
        x = (number of bytes - 1) * 2 + 11
   x+1..x+2     Checksum in hex notation
   x+3..x+4     Carriage return, line feed
 *
 */
STATIC_INLINE RC ihex_read_line( FILE *fh, struct ihex_line *line )
{
	S16 dat;
	U8 *p=&line->data[0];
	U16 count;
	S16 data;

	dat = fgetc(fh);
	if ((S16)(':') != dat) {
	   return RC_ERROR;
	}

	line->size=ihex_read_byte(fh);
	line->address=(ihex_read_byte(fh)<<8)|ihex_read_byte(fh);
	line->record_type=ihex_read_byte(fh);
	for(count=0;count<line->size;count++)
	{
		*p=ihex_read_byte(fh);
		p++;
	}
	ihex_read_byte(fh);
	data=fgetc(fh);
	while('\n' != data && EOF != data)
	{
		data=fgetc(fh);
	}

	return RC_SUCCESS;
}




STATIC_INLINE S8 ihex_hex_char(CH value )
{
   S8 rv;

   if( (value>='0') && (value <='9') )
   {
      rv = value-'0';
   }
   else
   {
      if( (value>='A') && (value <='F') )
      {
         rv =  value-'A'+10;
      }
      else
      {
         if( (value>='a') && (value <='f') )
         {
            rv =  value-'a'+10;
         }
         else
         {
            rv =  -1;
         }
      }
   }

   return rv;
}


STATIC_INLINE S16 ihex_byte( U8 d1, U8 d2 )
{

/*	if(( d1<0 )||(d2<0))
	{
		return -1;
	}*/

	return d1<<4|d2;
}

STATIC_INLINE S16 ihex_read_byte( FILE *fh )
{

	S16 data1;
	S16 data2;

	data1 = fgetc( fh );
	data2 = fgetc( fh );

	return ihex_byte( ihex_hex_char(data1),ihex_hex_char(data2) );
}



/***********************************************************************************************************************
*                             Copyright 2010 Linde Material Handling. All rights reserved.                             *
***********************************************************************************************************************/
